﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
using Daphne.Modularity.Storage;
using Microsoft.Practices.Unity;
using Daphne.Modularity.Messaging;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.Devices.Input;


namespace Daphne.Modularity.UI
{
    /// <summary>
    /// Attribute for marking control to be completed with dependencies in the visual tree
    /// </summary>
    public class DependencyControlAttribute: System.Attribute
    {
    }

    /// <summary>
    /// Base class for pages
    /// </summary>
    public class PageBase: Page
    {
        /// <summary>
        /// Dependency-injection container raface
        /// </summary>
        [Dependency]
        protected IUnityContainer Unity
        { get; set; }

        /// <summary>
        /// Communication channel reference
        /// </summary>
        [Dependency]
        protected IMessenger Messenger
        { get; set; }

        /// <summary>
        /// Reference to the storage service to keep the state of this page
        /// </summary>
        [Dependency]
        protected IStorageService StorageService
        { get; set; }

        /// <summary>
        /// Reference to the region manager
        /// </summary>
        [Dependency]
        protected IRegionManager RegionManagerInstance
        { get; set;  }

        /// <summary>
        /// Constructor
        /// </summary>
        public PageBase()
        {          
            // complete dependencies for this instance
            // TO-DO: move to the another class - possibly default Unity
            RegionManager.ResolveInstance(this);

            this.Loaded += PageBase_Loaded;
        }

        /// <summary>
        /// Called to resolve dependencies of child controls
        /// and then initialize them
        /// </summary>
        void PageBase_Loaded(object sender, RoutedEventArgs e)
        {
            // resolve child-controls dependencies
            CompleteDependecyChildren(this);

            // resolve viewmodel
            this.DataContext = RegionManagerInstance.ResolveViewModel(this.GetType());

            // initialize page when necessary
            if (this is IInitialization)
                (this as IInitialization).OnInitialized();

            // initialize view model if exists
            if (this.DataContext != null && this.DataContext is IInitialization)
                (this.DataContext as IInitialization).OnInitialized();
            
            // initialize child controls
            InitializeChildControls(this);
        }
     

        /// <summary>
        /// Traverses the visual tree to complete all controls
        /// </summary>
        private void CompleteDependecyChildren(DependencyObject depObject)
        {
            // get children count
            int count = VisualTreeHelper.GetChildrenCount(depObject);

            // iterate children
            for(int i = 0; i < count; i++)
            {
                // get dependency child
                DependencyObject depChild = VisualTreeHelper.GetChild(depObject, i);

                // check dependency attribute
                var attrList = depChild.GetType().GetTypeInfo().GetCustomAttributes(typeof(DependencyControlAttribute), true).ToArray();
                if(attrList.Length > 0)
                {
                    // if item has DependecyControlAttribute, the complete it with dependencies
                    RegionManager.ResolveInstance(depChild);
                }

                // probe visual children of iterated DependencyObject
                CompleteDependecyChildren(depChild);
            } // for
        }

        /// <summary>
        /// Traverses the visual tree to initialize child controls 
        /// after resolving their dependencies
        /// </summary>
        private void InitializeChildControls(DependencyObject depObject)
        {
            // get children count
            int count = VisualTreeHelper.GetChildrenCount(depObject);

            // iterate children
            for (int i = 0; i < count; i++)
            {
                // get dependency child
                DependencyObject depChild = VisualTreeHelper.GetChild(depObject, i);

                // check required interface
                if(depChild is IInitialization)
                {
                    // call to initialize
                    (depChild as IInitialization).OnInitialized();
                }

                // probe visual children of iterated DependencyObject
                InitializeChildControls(depChild);
            } // for
        }


        /// <summary>
        /// Method invoked to load the state
        /// </summary>
        private void Load()
        {
            // get page type
            Type type = this.GetType();

            // get the settings
            ClientStorage stateVM;
            PageState statePage;
            StorageService.GetPageState(type.FullName, out statePage, out stateVM);

            // invoke page state loading
            if(statePage != null)
            {
                LoadState(statePage);
            }

            // check ViewModel to load state
            if(DataContext != null && DataContext is ISaveRestore && stateVM != null)
            {
                stateVM.ClearKeyPrefix();
                (DataContext as ISaveRestore).LoadState(stateVM);
            }

            // invoke load on child controls
            if(stateVM != null)
            {
                InvokeLoadOnChilds(this, stateVM);
            }
        }

        /// <summary>
        /// Method invoked to save the state
        /// </summary>
        private void Save()
        {
            // get page type
            Type type = this.GetType();

            // get the settings
            ClientStorage stateVM = new ClientStorage();
            PageState statePage = new PageState();

            // save page
            SaveState(statePage);

            // check viewmodel if it requires to save state
            if (DataContext != null && DataContext is ISaveRestore)
            {
                stateVM.ClearKeyPrefix();
                (DataContext as ISaveRestore).SaveState(stateVM);
            }

            // invoke save on child controls
            InvokeSaveOnChilds(this, stateVM);

            // set the configuration to the storage service
            StorageService.SetPageState(type.FullName, statePage, stateVM);
        }

        /// <summary>
        /// Tranverses recursively the visual tree of the page to invoke Load on child-controls
        /// </summary>
        private void InvokeLoadOnChilds(DependencyObject depObject,  ClientStorage storage)
        {
            // get children count
            int count = VisualTreeHelper.GetChildrenCount(depObject);

            // iterate children
            for (int i = 0; i < count; i++)
            {
                // get dependency child
                DependencyObject depChild = VisualTreeHelper.GetChild(depObject, i);

                // check interface to load config
                if(depChild is ISaveRestore)
                {
                    // load config
                    storage.KeyPrefix = depChild.GetType().FullName;
                    (depChild as ISaveRestore).LoadState(storage);
                }

                // test if viewmodel should perform load operation
                if (depChild is FrameworkElement)
                {
                    FrameworkElement frmElem = depChild as FrameworkElement;
                    if (frmElem.DataContext != null && frmElem.DataContext is ISaveRestore)
                    {
                        storage.KeyPrefix = frmElem.DataContext.GetType().FullName;
                        (frmElem.DataContext as ISaveRestore).LoadState(storage);
                    }
                }

                // probe visual children of iterated DependencyObject
                InvokeLoadOnChilds(depChild, storage);
            } // for
        }

        /// <summary>
        /// Tranverses recursively the visual tree of the page to invoke Save on child-controls
        /// </summary>
        private void InvokeSaveOnChilds(DependencyObject depObject, ClientStorage storage)
        {
            // get children count
            int count = VisualTreeHelper.GetChildrenCount(depObject);

            // iterate children
            for (int i = 0; i < count; i++)
            {
                // get dependency child
                DependencyObject depChild = VisualTreeHelper.GetChild(depObject, i);

                // check interface to load config
                if (depChild is ISaveRestore)
                {
                    // load config
                    storage.KeyPrefix = depChild.GetType().FullName;
                    (depChild as ISaveRestore).SaveState(storage);
                }

                // test if viewmodel should perform save operation
                if (depChild is FrameworkElement)
                {
                    FrameworkElement frmElem = depChild as FrameworkElement;
                    if (frmElem.DataContext != null && frmElem.DataContext is ISaveRestore)
                    {
                        storage.KeyPrefix = frmElem.DataContext.GetType().FullName;
                        (frmElem.DataContext as ISaveRestore).SaveState(storage);
                    }
                }

                // probe visual children of iterated DependencyObject
                InvokeSaveOnChilds(depChild, storage);
            } // for
        }

        /// <summary>
        /// Invokes saving action of the page and its viewmodel
        /// </summary>
        protected override void OnNavigatedFrom(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
        {
            // save page state
            Save();

            base.OnNavigatedFrom(e);
        }

        /// <summary>
        /// Invokes loading action of the page state
        /// </summary>
        protected override void OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
        {
            // load page state
            Load();

            base.OnNavigatedTo(e);
        }
        
        /// <summary>
        /// Virtual method for loading state of page
        /// </summary>
        protected virtual void LoadState(PageState state)
        {
        }

        /// <summary>
        /// Virtual method for saving state of page
        /// </summary>
        protected virtual void SaveState(PageState state)
        {
        }

        /// <summary>
        /// Go back gesture fired
        /// </summary>
        protected virtual void GoBackFired()
        {
        }

        /// <summary>
        /// Go forward gesture fired
        /// </summary>
        protected virtual void GoForwardFired()
        {
        }
    }
}
